use hirun::runtime::{block_on, Builder, JoinSet};
use std::fmt::Debug;
use std::fs;
use std::path::Path;
use std::time::Instant;

fn main() {
    Builder::new().build().unwrap();
    for n in 0.. {
        let now = Instant::now();
        if let Ok(lines) = block_on(count_entry()) {
            println!(
                "lines = {lines} cpu = {}",
                hirun::thread::get_cpu_count()
            );
            println!("{n} time: {:?}", now.elapsed());
        } else {
            println!("Error!");
        }
    }
}

async fn count_entry() -> usize {
    let mut set = JoinSet::new();
    let mut lines = 0;
    let cnt = 10;
    for _ in 0..cnt {
        let mut args = std::env::args();
        let _ = set.spawn(visit_dir("."));
        args.next();
        for arg in args {
            let _ = set.spawn(visit_dir(arg));
        }
    }
    for (_, ret) in set.wait_all().await {
        lines += ret.unwrap();
    }
    lines
}

#[async_recursion::async_recursion]
async fn visit_dir<P: AsRef<Path> + Send + Debug>(dir: P) -> usize {
    let mut lines: usize = 0;
    let mut set = JoinSet::new();
    let Ok(dirs) = fs::read_dir(dir) else { return 0; };
    for entry in dirs {
        let path;
        match entry {
            Err(_) => continue,
            Ok(dir) => path = dir.path(),
        }
        if path.is_symlink() {
            continue;
        }
        if path.is_dir() {
            let _ = set.spawn(visit_dir(path));
        } else if path.is_file() {
            let _ = set.spawn(count_lines(path));
        }
    }

    for (_, ret) in set.wait_all().await {
        lines += ret.unwrap();
    }
    lines
}

async fn count_lines<P: AsRef<Path> + Send + Debug>(path: P) -> usize {
    let Ok(data) = fs::read(path) else { return 0; };
    let mut lines = if data.is_empty() { 0 } else { 1 };
    for b in data {
        if b == b'\n' {
            lines += 1;
        }
    }
    lines
}
